/****************************************************************************************/
/*                                                                     					*/
/*  M16C/62P Group Program Collection                                  					*/
/*                                                                     					*/
/*  FILE NAME : xmodem.c                        		              					*/
/*  CPU       : This program is the Serial flash boot loader by Xmodem data transfer	*/
/*  HISTORY   : 2006.12.08 Ver 0.01                                    					*/
/*                                                                    	 				*/
/*  Copyright (C) 2006. Renesas Technology Corp.                       					*/
/*  Copyright (C) 2006. Renesas Solutions Corp.                        					*/
/*  All right reserved.                                                					*/
/*                                                                     					*/
/*****************************************************************************************/

/**************************************/
/*    include file                    */
/**************************************/
#include <string.h>
#include "xmodem.h"
#include "flash_header.h"
#include "serial.h"
#include "command.h"

#pragma SECTION program ram_based_prg

/***********************************************/
/*    XmodemDownloadAndProgramFlash function   */
/***********************************************/
unsigned char XmodemDownloadAndProgramFlash (unsigned long FlashAddress)
{
/*
XmodemDownloadAndProgramFlash() takes a memory address as the base address to
which data downloaded is programmed.  The data is downloaded using the XModem
protocol developed in 1982 by Ward Christensen.
The routine detects errors due to timeouts, comms errors or invalid checksums.
The routine reports the following to the caller:
-Success
-Invalid address
-Comms error
-Timeout error
-Failure to program flash


Expects:	
--------
FlashAddress:
32-bit address located in Flash memory space starting on a 32-byte boundary

Returns:
--------
XM_OK				-	Download and Flash programming performed ok
XM_ADDRESS_ERROR	-	Address was either not on a 128-bit boundary or not in valid Flash
XM_COMMS_ERROR		-	Comms parity, framing or overrun error
XM_TIMEOUT			-	Transmitter did not respond to this receiver
XM_PROG_FAIL		-	Falied to program one or more bytes of the Flash memory
*/
	union union_c2s c2s;
	unsigned char ExpectedBlkNum;
	unsigned char RetryCounter;
	unsigned char RxByteCount;
	unsigned char RxByteBufferIndex;
	unsigned char Status;
	unsigned char checksum;
	unsigned char StartCondition;
	unsigned long Address;
	volatile union {
		unsigned char uc[ 128 + 4 + 1 ];
		read_datum ur[ (128 + 4 + 1) / sizeof (read_datum) ];
	} RxByteBuffer; 
	// To ensure that data is stored starting on the correct size boundary for the Flash
	// an extra byte is needed as this buffer must store the xmodem protocol bytes
	// and the data bytes.	This means that a padding byte is added
	// at the beginning of the buffer.

	// first xmodem block number is 1
	ExpectedBlkNum = 1;
	
	StartCondition = 0;
	
	Address = FlashAddress;
	
	// if required, copy the RAM based program function to RAM
	// in this version the copy procedure has already been done in 'InitCommandHandler' in 'command.c'
			
	// flush the comms rx buffer with a delay of 1 sec
	// function will return when no data has been received for 1 sec
	PurgeComms( 1000 );
		
		
	while(1)
	{
		//	{1}
		//	initialise Rx attempts
		RetryCounter = 10;
	
		//	decrement Rx attempts counter & get Rx byte with a 10 sec timeout repeat until Rx attempts is 0
		c2s.uc[0] = TIMEOUT;
		while ( (RetryCounter > 0) && (c2s.uc[0] == TIMEOUT) )
		{
			if (StartCondition == 0)
			{
				//	if this is the start of the xmodem frame
				//	send a NAK to the transmitter
				SendByte( NAK );
				c2s.us = GetByte( 10000 );
			}							  
			else
			{
				c2s.us = GetByte( 1000 );
			}
			RetryCounter--;
		}
			
		StartCondition = 1;
	
		//	if timed out after 10 attempts or comms error
		//	return relevant error state to caller
		if ( c2s.uc[0] == TIMEOUT )
		{
			return ( XM_TIMEOUT ); 
		}
		else if ( c2s.uc[0] == ERROR )
		{
			// loop back to (1)
			// do nothing
			return ( XM_COMMS_ERROR );
		}
		else			
		{
			// if first received byte is 'end of frame'
			// return ACK to sender and exit
			if ( c2s.uc[1] == EOT )
			{
				SendByte( ACK );
				return( XM_OK );
			}
			else
			{				
				//	initialise counter for incoming Rx bytes
				// start of header + block num + (255 - block num) + 128 data bytes + checksum
				RxByteCount = 128 + 4;
				// RxByteBufferIndex is initiales to 1 to ensure correct boundary for the data
				RxByteBufferIndex = 1;
				
				Status = XM_OK;
					
				// store the byte we have just received
				RxByteBuffer.uc[ RxByteBufferIndex++ ] = c2s.uc[1];
				RxByteCount--;
					
				while( RxByteCount > 0 )
				{
					//	get Rx byte with 1 second timeout
					c2s.us = GetByte( 1000 );

					//	if timed out or comms error
					if ( (c2s.uc[0] == TIMEOUT) || (c2s.uc[0] == ERROR) )
					{
						Status = XM_TIMEOUT;
						//	timed out so purge incoming data
						PurgeComms( 1000 );
						// send NAK and return loop back start of while loop
						SendByte( NAK );
						RxByteCount = 0;
					}
					else
					{
						// no timeout or comms error
						// store Rx byte
						RxByteBuffer.uc[ RxByteBufferIndex++ ] = c2s.uc[1];
						RxByteCount--;
					}
				}
					
				if (Status == XM_TIMEOUT)
				{
						// loop back to (1)
						// do nothing
				}
				else
				{
					// data Rx ok
					// calculate the checksum of the data bytes only
					checksum = 0;
					for (RxByteBufferIndex=0; RxByteBufferIndex<128; RxByteBufferIndex++)
					{
						checksum += RxByteBuffer.uc[ RxByteBufferIndex + 3 + 1 ];
					}

					//	if SOH, BLK#, 255-BLK# or checksum not valid
					//	(BLK# is valid if the same as expected blk counter or is 1 less
					if ( !( (RxByteBuffer.uc[0 + 1] == SOH) && ((RxByteBuffer.uc[1 + 1] == ExpectedBlkNum) || (RxByteBuffer.uc[1 + 1] == ExpectedBlkNum - 1) ) && (RxByteBuffer.uc[2 + 1] + RxByteBuffer.uc[1 + 1] == 255 ) && (RxByteBuffer.uc[131 + 1] == checksum) ) )
					{
						//	send NAK and loop back to (1)
						SendByte( NAK );
					}
					else
					{
						//	if blk# is expected blk num
						if ( RxByteBuffer.uc[1 + 1] == ExpectedBlkNum )
						{
							// check the addresses to be programmed are within the user flash area
							if( ( Address < FIRST_USER_FLASH_ADDR ) || ( ( Address + 128 ) > LAST_USER_FLASH_ADDR ) )
							{
								// invalid flash addresses
								// prog fail
								SendByte( NAK );
								// cancel xmodem download
								SendByte( CAN );
									
								return( XM_ADDRESS_ERROR );
							}
							
							//	call the flash prog routine with the Rx data and address
							Status = Program_Bytes( Address, 128, (unsigned short *) &RxByteBuffer.uc[3 + 1] );
		
							if( Status == PROG_PASS )
							{
								//	if prog routine passed ok increment flash address by 128
								Address += 128;
								ExpectedBlkNum++;
								SendByte( ACK );

								//	loop back to (1)
							}
							else
							{
								// prog fail
								SendByte( NAK );
								// cancel xmodem download
								SendByte( CAN );
									
								return( XM_PROG_FAIL );
							}
						}
						else
						{
							//	block number is valid but this data block has already been received
							//	send ACK and loop to (1)
							SendByte( ACK );
						}
					}
				}
			}
		}
	}		

	return(XM_OK);		//	Processing doesn't come here. 
						//	The purpose is to erase the Warning output of the compiler. 

}
